001 /* 002 * Copyright 2006 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.util; 020 021 import java.util.EventObject; 022 import java.util.List; 023 import java.util.LinkedList; 024 025 import net.dpml.transit.monitor.LoggingAdapter; 026 027 /** 028 * A abstract base class that established an event queue and handles event dispatch 029 * operations for listeners declared in classes extending this base class. 030 * 031 * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a> 032 * @version 1.0.0 033 */ 034 public class EventQueue 035 { 036 // ------------------------------------------------------------------------ 037 // state 038 // ------------------------------------------------------------------------ 039 040 private final EventDispatchThread m_thread; 041 042 private final Logger m_logger; 043 044 private final List m_queue; 045 046 // ------------------------------------------------------------------------ 047 // constructor 048 // ------------------------------------------------------------------------ 049 050 /** 051 * Creation of a new event queue. 052 * @param name the name used to construct a logging channel 053 */ 054 public EventQueue( String name ) 055 { 056 this( getLoggerForCategory( name ) ); 057 } 058 059 /** 060 * Creation of a new model. 061 * @param logger the assigned logging channel 062 * @exception NullPointerException if the supplied logging channel is null 063 */ 064 public EventQueue( Logger logger ) 065 throws NullPointerException 066 { 067 if( null == logger ) 068 { 069 throw new NullPointerException( "logger" ); 070 } 071 m_logger = logger; 072 m_queue = new LinkedList(); 073 m_thread = new EventDispatchThread(); 074 m_thread.setDaemon( true ); 075 m_thread.start(); 076 } 077 078 // ------------------------------------------------------------------------ 079 // EventQueue 080 // ------------------------------------------------------------------------ 081 082 /** 083 * Terminate the dispatch thread. 084 */ 085 public synchronized void terminateDispatchThread() 086 { 087 if( null != m_thread ) 088 { 089 m_thread.dispose(); 090 } 091 } 092 093 /** 094 * Return the assigned logging channel. 095 * @return the logging channel 096 */ 097 private Logger getLogger() 098 { 099 return m_logger; 100 } 101 102 /** 103 * A single background thread ("the event notification thread") monitors 104 * the event queue and delivers events that are placed on the queue. 105 */ 106 private class EventDispatchThread extends Thread 107 { 108 private boolean m_continue = true; 109 110 void dispose() 111 { 112 synchronized( m_queue ) 113 { 114 m_continue = false; 115 m_queue.notify(); 116 } 117 } 118 119 public void run() 120 { 121 while( m_continue ) 122 { 123 // Wait on m_queue till an event is present 124 EventObject event = null; 125 synchronized( m_queue ) 126 { 127 try 128 { 129 while( m_continue && m_queue.isEmpty() ) 130 { 131 m_queue.wait(); 132 } 133 if ( !m_continue ) 134 { 135 break; 136 } 137 Object object = m_queue.remove( 0 ); 138 try 139 { 140 event = (EventObject) object; 141 } 142 catch( ClassCastException cce ) 143 { 144 final String error = 145 "Unexpected class cast exception while processing an event." 146 + "\nEvent: " + object; 147 throw new IllegalStateException( error ); 148 } 149 } 150 catch( InterruptedException e ) 151 { 152 return; 153 } 154 } 155 156 Object source = event.getSource(); 157 if( source instanceof EventHandler ) 158 { 159 EventHandler handler = (EventHandler) source; 160 try 161 { 162 handler.processEvent( event ); 163 } 164 catch( Throwable e ) 165 { 166 final String error = 167 "Unexpected error while processing event." 168 + "\nEvent: " + event 169 + "\nSource: " + this; 170 getLogger().error( error, e ); 171 } 172 } 173 else 174 { 175 final String error = 176 "Event source is not an instance of " 177 + EventHandler.class.getName(); 178 getLogger().error( error ); 179 } 180 } 181 } 182 } 183 184 /** 185 * Enqueue an event for delivery to registered 186 * listeners unless there are no registered 187 * listeners. 188 * 189 * @param event the event object to add to the queue 190 */ 191 public void enqueueEvent( EventObject event ) 192 { 193 enqueueEvent( event, false ); 194 } 195 196 /** 197 * Enqueue an event for delivery to registered 198 * listeners unless there are no registered 199 * listeners. 200 * 201 * @param event the event object to add to the queue 202 * @param waitForCompletion if TRUE the implementation will apply 203 * the event to the event source event handler and return on 204 * copmpletion of evetn delivery 205 */ 206 public void enqueueEvent( EventObject event, boolean waitForCompletion ) 207 { 208 if( !waitForCompletion ) 209 { 210 synchronized( m_queue ) 211 { 212 m_queue.add( event ); 213 m_queue.notify(); 214 } 215 } 216 else 217 { 218 Object source = event.getSource(); 219 if( source instanceof EventHandler ) 220 { 221 EventHandler handler = (EventHandler) source; 222 try 223 { 224 handler.processEvent( event ); 225 } 226 catch( Throwable e ) 227 { 228 final String error = 229 "Unexpected error while processing event." 230 + "\nEvent: " + event 231 + "\nSource: " + source; 232 getLogger().error( error ); 233 } 234 } 235 else 236 { 237 final String error = 238 "Event source is not an instance of " 239 + EventHandler.class.getName() 240 + "\nSource: " + source.getClass().getName(); 241 throw new IllegalStateException( error ); 242 } 243 } 244 } 245 246 /** 247 * Return a logging channel for the supplied name. 248 * @param name the name to use in construction of the logging channel 249 * @return the logging channel 250 */ 251 static Logger getLoggerForCategory( String name ) 252 { 253 if( null == name ) 254 { 255 return new LoggingAdapter( "" ); 256 } 257 else 258 { 259 return new LoggingAdapter( name ); 260 } 261 } 262 }